package com.hcc.flow.server.service.workbench;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSONObject;
import com.hcc.flow.server.common.advice.RRException;
import com.hcc.flow.server.common.constant.Constant;
import com.hcc.flow.server.common.constant.RedisKeyConstant;
import com.hcc.flow.server.common.enums.CheckStatusType;
import com.hcc.flow.server.common.enums.MsgType;
import com.hcc.flow.server.common.model.ApiResult;
import com.hcc.flow.server.common.utils.CommUtil;
import com.hcc.flow.server.common.utils.StringUtilsV2;
import com.hcc.flow.server.common.vo.CarouselMsgVO;
import com.hcc.flow.server.common.vo.SelectVO;
import com.hcc.flow.server.common.vo.SelectValueVO;
import com.hcc.flow.server.dao.flowDesign.FlowDesignDao;
import com.hcc.flow.server.dao.flowDesign.FlowTaskTypeDao;
import com.hcc.flow.server.dao.flowTask.AdContentDao;
import com.hcc.flow.server.dao.flowTask.CustomerDao;
import com.hcc.flow.server.dao.sys.OrgDao;
import com.hcc.flow.server.dao.sys.UserDao;
import com.hcc.flow.server.dao.workbench.MsgNoticeDao;
import com.hcc.flow.server.dao.workbench.MsgNoticeReadDao;
import com.hcc.flow.server.dto.sys.VerifyDto;
import com.hcc.flow.server.dto.workbench.MsgNoticeDto;
import com.hcc.flow.server.model.common.LoginUser;
import com.hcc.flow.server.model.flowDesign.FlowDesign;
import com.hcc.flow.server.model.flowDesign.FlowDesignTemp;
import com.hcc.flow.server.model.flowDesign.FlowTaskType;
import com.hcc.flow.server.model.flowTask.AdContent;
import com.hcc.flow.server.model.flowTask.Customer;
import com.hcc.flow.server.model.sys.Org;
import com.hcc.flow.server.model.sys.SysAuditLogs;
import com.hcc.flow.server.model.workbench.MsgNotice;
import com.hcc.flow.server.model.workbench.MsgNoticeRead;
import com.hcc.flow.server.service.flowDesign.FlowService;
import com.hcc.flow.server.service.sys.RedisServer;
import com.hcc.flow.server.service.sys.SysLogService;
import com.hcc.flow.server.service.sys.UserService;
import com.hcc.flow.server.utils.UserUtil;
import com.hcc.flow.server.vo.flowTask.AdContentVO;
import com.hcc.flow.server.vo.flowTask.CustomerVO;
import com.hcc.flow.server.vo.sys.SysUserDetailVO;
import com.hcc.flow.server.vo.workbench.MsgNoticeReadToUserVO;
import com.hcc.flow.server.vo.workbench.MsgNoticeReadVO;

import net.sf.json.util.JSONUtils;

@Service
public class MsgNoticeService {

	private static final Logger log = LoggerFactory.getLogger("adminLogger");
	@Autowired
	private MsgNoticeDao msgNoticeDao;
	@Autowired
	private MsgNoticeReadDao msgNoticeReadDao;
	@Autowired
	private UserDao userDao;
	@Autowired
	private UserService userService;
	@Autowired
	private FlowDesignDao flowDao;
	@Autowired
	private FlowService flowService;
	@Autowired
	private AdContentDao adContentDao;
	@Autowired
	private SysLogService logService;
	@Autowired
	private CustomerDao customerDao;
	@Autowired
	private OrgDao orgDao;
	@Autowired
	private FlowTaskTypeDao flowTaskTypeDao;
	
	/**
	 * token过期秒数
	 */
	@Value("${token.expire.seconds}")
	private Long expireSeconds;

	/**
	 * 发送消息
	 */
	
	@Transactional
	public ApiResult<?> save(MsgNoticeDto notice) {
		LoginUser user = UserUtil.getLoginUser();
		if (StringUtilsV2.isBlank(notice.getPushUserId())) {
			notice.setPushUserId(user.getUserId());
		}
		if (StringUtilsV2.isBlank(notice.getType())) {
			return ApiResult.error("消息类型不能为空");
		}
		if (StringUtilsV2.isBlank(notice.getTitle())) {
			return ApiResult.error("消息标题不能为空");
		}
		if (StringUtilsV2.isBlank(notice.getContent())) {
			return ApiResult.error("消息内容不能为空");
		}
		StringBuffer verifyRemarks = new StringBuffer();
		String taskType = "";
		try {
			List<String> collectUserIds = notice.getCollectUserIds();
			if (MsgType.TASK.getCode().equals(notice.getType())) {
				taskType = notice.getTaskType();
				if (StringUtilsV2.isBlank(taskType)) {
					return ApiResult.error("具体任务消息类型不能为空");
				}
				if (CollectionUtils.isEmpty(collectUserIds)) {
					FlowDesignTemp flowTemp = null;
					ApiResult<JSONObject> nextNodeAr = null;
					String flowOrgName = null;
					if (StringUtilsV2.isBlank(notice.getFlowTempId()) && StringUtilsV2.isBlank(notice.getFlowTempNodeId())) {
						String flowOrgId = notice.getFlowOrgId();
						if(StringUtilsV2.isBlank(flowOrgId)) {
							flowOrgId = user.getOrgId();
							flowOrgName = user.getOrgName();
						}else {
							Org o = orgDao.getById(flowOrgId);
							if(o != null) {
								flowOrgName = o.getOrgName();
							}else {
								flowOrgName = flowOrgId;
							}
						}
						FlowDesign flow = null;
						//是订单模块并且是提交平台媒体审核任务操作
						flow = flowDao.getOneByOrgIdAndStatusAndAttribute(flowOrgId, taskType,Constant.DATA_STATUS_TYPE_NORMAL, notice.getFlowAttribute());
						if (flow == null) {
							FlowTaskType flowTaskType= flowTaskTypeDao.getById(taskType);
		    				if(flowTaskType == null){
		    					return ApiResult.error("通过类型ID("+taskType+")未找到对应流程业务类型");
		    				}
		    				return ApiResult.error("发送'" + flowTaskType.getTaskTypeName() + "审核'任务失败,原因:未找到当前机构(" + flowOrgName + ")对应启用状态的'" + flowTaskType.getTaskTypeName() + "'审核流程，请联系平台方或者机构管理员创建");
						}
						flowTemp = flowService.getTempByFlowId(flow.getFlowId());
						if (flowTemp == null) {
							return ApiResult.error("未找到流程设计模板实时数据");
						}
						nextNodeAr = flowService.upOrDownNode(null, flowTemp.getFlowData(), null, 1);
					} else {
						flowTemp = flowService.getTempById(notice.getFlowTempId());
						if (flowTemp == null) {
							return ApiResult.error("未找到流程设计模板实时数据");
						}
						nextNodeAr = flowService.upOrDownNode(null, flowTemp.getFlowData(), notice.getFlowTempNodeId(),1);
						FlowDesign flow = flowDao.getById(flowTemp.getFlowId());
						if (flow == null) {
							return ApiResult.error("未找到流程设计模板原始数据");
						}
						Org o = orgDao.getById(flow.getFollowOrgId());
						if(o == null) {
							return ApiResult.error("未找到流程设计模板原始数据对应的归属机构");
						}
						flowOrgName = o.getOrgName();
					}
					if (!nextNodeAr.isSuccess()) {
						return nextNodeAr;
					}
					notice.setFlowTempId(flowTemp.getFlowTempId());
					notice.setFlowTempNodeId(nextNodeAr.getMsg().toString());
					JSONObject node = nextNodeAr.getData();
					if (node == null) {
						return ApiResult.error("未找到流程任务审核节点");
					}
					String nodeName = StringUtilsV2.isNotBlank(node.getString("name")) ? node.getString("name") : nextNodeAr.getMsg().toString();
					String verifyType = node.getString("verifyType");
					if (StringUtilsV2.isBlank(verifyType)) {
						return ApiResult.error("流程(" + flowTemp.getFlowName() + ")审核节点(" + nodeName + ")的任务归属类型不能为空");
					}
					String verifyModelIds = node.getString("verifyModelIds");
					StringBuffer verifyModelNames = new StringBuffer(node.getString("verifyModelNames"));
					if (StringUtilsV2.isBlank(verifyModelIds)) {
						return ApiResult.error("流程(" + flowTemp.getFlowName() + ")审核节点(" + nodeName + ")的任务归属者不能为空");
					}
					if ("3".equals(verifyType)) {//用户
						collectUserIds = getUsers(Arrays.asList(verifyModelIds.split(",")), null, null).getData();
						if (CollectionUtils.isEmpty(collectUserIds)) {
							return ApiResult.error(MsgType.TASK.getName() + "发送失败,流程(" + flowTemp.getFlowName() + ")的审核节点(" + nodeName + ")未指定接收审核任务的用户");
						}
						verifyModelNames.insert(0, "用户[").append("]");
					} else if ("2".equals(verifyType)) {//角色
						collectUserIds = getUsers(null, Arrays.asList(verifyModelIds.split(",")), null).getData();
						if (CollectionUtils.isEmpty(collectUserIds)) {
							return ApiResult.error(MsgType.TASK.getName() + "发送失败,流程(" + flowTemp.getFlowName() + ")的审核节点(" + nodeName + ")下指定角色(" + node.getString("verifyModelNames") + ")下未找到接收审核任务的用户");
						}
						verifyModelNames.insert(0, "角色[").append("]");
					} else if ("1".equals(verifyType)) {//机构
						collectUserIds = getUsers(null, null, Arrays.asList(verifyModelIds.split(","))).getData();
						if (CollectionUtils.isEmpty(collectUserIds)) {
							return ApiResult.error(MsgType.TASK.getName() + "发送失败,流程(" + flowTemp.getFlowName() + ")的审核节点(" + nodeName + ")下指定机构(" + node.getString("verifyModelNames") + ")下未找到接收审核任务的用户");
						}
						verifyModelNames.insert(0, "机构[").append("]");
					}
					verifyRemarks.append("<br/>");
					verifyRemarks.append("流程归属机构: ").append(flowOrgName).append(",<br/>");
					verifyRemarks.append("流程名称: ").append(flowTemp.getFlowName()).append(",<br/>");
					verifyRemarks.append("任务节点: ").append(nodeName).append(node.getIntValue("verifyJoint")==1?"(会签)":"").append(",<br/>");
					verifyRemarks.append("节点归属: ").append(verifyModelNames);
				}
			} else if (MsgType.SYS.getCode().equals(notice.getType())) {
				if (notice.getShowBeginTime() == null) {
					return ApiResult.error("展示开始时间不能为空");
				}
				if (notice.getShowEndTime() == null) {
					return ApiResult.error("展示结束时间不能为空");
				}
				if (notice.getShowBeginTime().compareTo(notice.getShowEndTime()) != -1) {
					return ApiResult.error("展示开始时间必须小于展示结束时间");
				}
				collectUserIds = userDao.listAllUserIds();
				if (CollectionUtils.isEmpty(collectUserIds)) {
					return ApiResult.error(MsgType.SYS.getName() + "发送失败,全平台用户数据丢失");
				}
			} else if (MsgType.MSG.getCode().equals(notice.getType())) {
				if (CollectionUtils.isEmpty(collectUserIds) && CollectionUtils.isEmpty(notice.getRoleIds()) && CollectionUtils.isEmpty(notice.getOrgIds())) {
					collectUserIds = userService.getListUserIdsByOrgId(null);// 查找登陆机构和旗下所有子机构下所有用户
					if (CollectionUtils.isEmpty(collectUserIds)) {
						return ApiResult.error(MsgType.MSG.getName() + "发送失败,登陆机构和旗下所有子机构下没有用户");
					}
				} else {
					collectUserIds = getUsers(collectUserIds, notice.getRoleIds(), notice.getOrgIds()).getData();
					if (CollectionUtils.isEmpty(collectUserIds)) {
						if (CollectionUtils.isNotEmpty(notice.getRoleIds())) {
							return ApiResult.error(MsgType.MSG.getName() + "发送失败,未找到指定角色下接收站内信的用户数据");
						} else if (CollectionUtils.isNotEmpty(notice.getRoleIds())) {
							return ApiResult.error(MsgType.MSG.getName() + "发送失败,未找到指定机构下接收站内信的用户数据");
						}
						return ApiResult.error(MsgType.MSG.getName() + "发送失败,未找到接收站内信的用户数据");
					}
				}
			} else if (MsgType.RESULT.getCode().equals(notice.getType())) {
				if (StringUtilsV2.isBlank(notice.getTaskType())) {
					return ApiResult.error("具体任务消息类型不能为空");
				}
				FlowTaskType flowTaskType= flowTaskTypeDao.getById(notice.getTaskType());
				if(flowTaskType == null){
					return ApiResult.error("通过类型ID("+notice.getTaskType()+")未找到对应流程业务类型");
				}
				notice.setHref(flowTaskType.getHref()+"?id="+notice.getFlowFromId());// 点击跳转路径
				if (CollectionUtils.isEmpty(collectUserIds)) {
					return ApiResult.error(flowTaskType.getTaskTypeName() + "状态消息推送失败,没有指明要接收推送消息的用户");
				}
				collectUserIds = getUsers(collectUserIds, notice.getRoleIds(), notice.getOrgIds()).getData();
				// collectUserIds.remove(notice.getPushUserId());
			}
			String msgType = MsgType.getType(notice.getType()).toString().toLowerCase();
			
			boolean send = true;
			// 同一任务消息是否已经发送过了
			if (MsgType.TASK.getCode().equals(notice.getType())) {
				List<MsgNoticeReadVO> dbMsgList = msgNoticeDao.getNoticeReadVOByMore(notice.getFlowFromId(), notice.getFlowSubmitNo(), notice.getFlowTempId(), notice.getFlowTempNodeId(),true,0);
				send = (dbMsgList.size() == 0);
			}
			if (send) {
				msgNoticeDao.save(notice);
				MsgNoticeRead msgNoticeRead = new MsgNoticeRead();
				msgNoticeRead.setNoticeId(notice.getNoticeId());
				msgNoticeRead.setFlowVerifyStatus(notice.getFlowVerifyStatus());
				msgNoticeRead.setFlowVerifyReason(notice.getFlowVerifyReason());
				Set<String> mkeys = new HashSet<>();
				for (String cuserId : collectUserIds) {
					if (StringUtilsV2.isNotBlank(cuserId)) {
						msgNoticeRead.setReadUserId(cuserId);
						msgNoticeReadDao.save(msgNoticeRead);
						mkeys.add(RedisKeyConstant.KEY_MSG_NOTICE + msgType+taskType+ ":" + cuserId);
					}
				}
				RedisServer.removeKeys(null,mkeys);
			}
		} catch (Exception e) {
			log.error("发送消息通知失败:{}", e.getMessage());
			return ApiResult.error("发送消息通知失败:" + e.getMessage());
		}
		return ApiResult.data(verifyRemarks);
	}

	
	public ApiResult<?> indexShow(String userId) {
		Map<String, Object> map = new HashMap<>();
		// 未读（处理）任务消息数
		Map<String, Object> taskMap = new HashMap<>();
		Integer task = 0;
		List<FlowTaskType> fts = flowTaskTypeDao.listAll();
		for (FlowTaskType m : fts) {
			Integer count = null;
			Object countObj = RedisServer.getCacheValue(null,RedisKeyConstant.KEY_MSG_NOTICE + "task" + m.getTaskTypeId() + ":" + userId);
			if (countObj == null) {
				count = msgNoticeReadDao.getCountByTaskType(userId, MsgType.TASK.getCode(),m.getTaskTypeId().toString());
				RedisServer.setCacheValue(null, RedisKeyConstant.KEY_MSG_NOTICE + "task" + m.getTaskTypeId() + ":" + userId, count, expireSeconds);
			}else{
				count = CommUtil.null2Int(countObj);
			}
			taskMap.put(m.getTaskTypeId().toString(), count);
			task += count;
		}
		taskMap.put("allCount", task);
		// 未读结果消息数
		Integer result = null;
		Object resultObj = RedisServer.getCacheValue(null,RedisKeyConstant.KEY_MSG_NOTICE + "result" + ":" + userId);
		if (resultObj == null) {
			result = msgNoticeReadDao.getCount(userId, MsgType.RESULT.getCode());
			RedisServer.setCacheValue(null, RedisKeyConstant.KEY_MSG_NOTICE + "result" + ":" + userId, result, expireSeconds);
		}else{
			result = CommUtil.null2Int(resultObj);
		}
		// 未读系统消息数
		Integer sys = null;
		Object sysObj = RedisServer.getCacheValue(null,RedisKeyConstant.KEY_MSG_NOTICE + "sys" + ":" + userId);
		if (sysObj == null) {
			sys = msgNoticeReadDao.getCount(userId, MsgType.SYS.getCode());
			RedisServer.setCacheValue(null, RedisKeyConstant.KEY_MSG_NOTICE + "sys" + ":" + userId, sys, expireSeconds);
		}else{
			sys = CommUtil.null2Int(sysObj);
		}
		// 未读站内信消息数
		Integer msg = null;
		Object msgObj = RedisServer.getCacheValue(null,RedisKeyConstant.KEY_MSG_NOTICE + "msg" + ":" + userId);
		if (msgObj == null) {
			msg = msgNoticeReadDao.getCount(userId, MsgType.MSG.getCode());
			RedisServer.setCacheValue(null,RedisKeyConstant.KEY_MSG_NOTICE + "msg" + ":" + userId, msg, expireSeconds);
		}else{
			msg = CommUtil.null2Int(msgObj);
		}
		// 消息类型（1任务消息2系统消息3站内信4结果消息）
		map.put("taskCount", taskMap);
		map.put("sysCount", sys);
		map.put("msgCount", msg);
		map.put("resultCount", result);
		map.put("allCount", task + result + sys + msg);
		// 系统滚动消息
		List<CarouselMsgVO> cmsgs;
		if(sys > 0) {
			/*//系统滚动消息每5分钟才查询一次数据库
			Long size = redisTemplateCMsg.opsForList().size(key + "cmsg");
			List<CarouselMsgVO> cmsgs;
			if (size == null || size.equals(0L)) {
				cmsgs = msgNoticeReadDao.getCarouselMsg(userId);
				if (cmsgs.size() == 0) {
					cmsgs = new ArrayList<>();
					CarouselMsgVO v = new CarouselMsgVO();
					v.setReadId("0");
					cmsgs.add(v);
				}
				redisTemplateCMsg.delete(key + "cmsg");
				redisTemplateCMsg.opsForList().rightPushAll(key + "cmsg", cmsgs);
				redisTemplateCMsg.expire(key + "cmsg", 500, TimeUnit.SECONDS);
			} else {
				cmsgs = redisTemplateCMsg.opsForList().range(key + "cmsg", 0, -1);
			}
			if (cmsgs.size() == 1 && "0".equals(cmsgs.get(0).getReadId())) {
				cmsgs.remove(0);
			}*/
			cmsgs = msgNoticeReadDao.getCarouselMsg(userId);
		}else {
			cmsgs = new ArrayList<CarouselMsgVO>();
		}
		map.put("carousel", cmsgs);
		return ApiResult.data(map);
	}

	/**
	 * 组装接收消息的用户id列表
	 * 
	 * @param collectUserIds
	 * @param roleIds
	 * @param OrgIds
	 * @return
	 */
	private ApiResult<List<String>> getUsers(List<String> collectUserIds, List<String> roleIds, List<String> OrgIds) {
		ApiResult<List<String>> result = new ApiResult<>();
		if (CollectionUtils.isEmpty(collectUserIds)) {
			if (!CollectionUtils.isEmpty(roleIds)) {
				collectUserIds = userService.getListUserIdsByRoleIdsNoChild(StringUtilsV2.join(roleIds, ","));// 查找指定角色ids下的所有用户
			} else if (!CollectionUtils.isEmpty(OrgIds)) {
				collectUserIds = userDao.listUserIdsByOrgIds(StringUtilsV2.join(OrgIds, ","));// 查找指定机构ids下的所有用户
			}
		}
		if (!CollectionUtils.isEmpty(collectUserIds)) {
			collectUserIds = collectUserIds.stream().distinct().collect(Collectors.toList());
			collectUserIds.remove(null);
		} else {
			collectUserIds = new ArrayList<String>();
		}
		result.setData(collectUserIds);
		return result;
	}

	/**
	 * 流程节点审核通过
	 * 
	 * @param verifyDto
	 * @return
	 */
	
	@Transactional
	public ApiResult<?> flowAudited(VerifyDto verifyDto) {
		LoginUser user = UserUtil.getLoginUser();
		MsgNoticeRead read = new MsgNoticeRead();
		MsgNotice currNotice = new MsgNotice();
		JSONObject currNode = new JSONObject();
		FlowDesignTemp flowTemp = new FlowDesignTemp();
		JSONObject flow = new JSONObject();
		ApiResult<Boolean> downNodeIsEndAr = new ApiResult<Boolean>();
		StringBuffer needVerifyAuditedNum = new StringBuffer();
		StringBuffer actualVerifyAuditedNum = new StringBuffer();
		ApiResult<?> verifyCheckAr = verifyCheck(verifyDto, user.getUserId(), read, currNotice, currNode, flowTemp,flow, downNodeIsEndAr, needVerifyAuditedNum, actualVerifyAuditedNum,user);
		if (!verifyCheckAr.isSuccess()) {
			return verifyCheckAr;
		}
		String currNodeName = StringUtilsV2.isNotBlank(currNode.getString("name")) ? currNode.getString("name") : currNotice.getFlowTempNodeId();
		FlowTaskType flowTaskType= flowTaskTypeDao.getById(currNotice.getTaskType());
		if(flowTaskType == null){
			return ApiResult.error("通过类型ID("+currNotice.getTaskType()+")未找到对应流程业务类型");
		}
		StringBuffer taskTypeName = new StringBuffer();// 业务类型名
		StringBuffer taskFromReamrks = new StringBuffer();// 业务主体说明
		StringBuffer downTaskPushUserId = new StringBuffer();// 下一级任务提交人
		List<String> collectUserIds = new ArrayList<>();// 审核结果通知接收人
		StringBuffer strChangeId = new StringBuffer();// 换刊创意id
		StringBuffer orderId = new StringBuffer();// 主订单id(换刊需要)
		StringBuffer orderDetId = new StringBuffer();// 子订单id(换刊需要)
		//StringBuffer flowFromId = new StringBuffer(verifyDto.getId());// 流程业务对象id
		ApiResult<?> verifyInfoAr = verifyInfo(verifyDto.getId(), flowTaskType, taskTypeName, taskFromReamrks, downTaskPushUserId, collectUserIds, strChangeId, orderId,orderDetId);
		if (!verifyInfoAr.isSuccess()) {
			return verifyInfoAr;
		}
		// 发送审核结果
		MsgNoticeDto notice = new MsgNoticeDto();
		notice.setType(MsgType.RESULT.getCode());
		notice.setTaskType(flowTaskType.getTaskTypeId().toString());
		notice.setTitle(taskTypeName + "审核结果(" + (downNodeIsEndAr.getData() ? "'终审'" : "'非终审'") + "通过)通知");
		notice.setCollectUserIds(collectUserIds);// 接收人
		StringBuffer sb = new StringBuffer()
				//.append("业务模板: ").append(taskTypeName).append(",<br>")
				.append("审核流程: " ).append(flowTemp.getFlowName()).append(",<br>")
				.append("任务节点: " ).append(currNodeName).append(",<br/>")
				.append("是否为终审: " ).append(downNodeIsEndAr.getData() ? "是" : "不是").append(",<br>")
				.append("审核结果: " ).append("审核通过").append(",<br/>")
				.append("审核说明: ").append(verifyDto.getRejectReason()).append(".<br>");
		StringBuffer reamrksPrefix = new StringBuffer("审核通过的").append(taskTypeName).append("详情: <br>");
		notice.setContent(sb.append(reamrksPrefix).append(taskFromReamrks).toString());
		notice.setFlowFromId(verifyDto.getId());
		notice.setFlowVerifyStatus(CheckStatusType.AUDITED.getCode());
		notice.setFlowVerifyReason(verifyDto.getRejectReason());
		notice.setFlowSubmitNo(currNotice.getFlowSubmitNo());
		notice.setFlowTempId(currNotice.getFlowTempId());
		notice.setFlowTempNodeId(currNotice.getFlowTempNodeId());
		ApiResult<?> apiResult = save(notice);
		if (!apiResult.isSuccess()) {
			throw new RRException(apiResult.getMsg());
		}
		//需要审核的用户数
		int needNum = CommUtil.null2Int(needVerifyAuditedNum.toString());
		//实际审核用户数
		int actualNum = (CommUtil.null2Int(actualVerifyAuditedNum.toString()) + 1);
		//是否通过该节点
		boolean nodeAdopt = (actualNum >= needNum);
		ApiResult<Boolean> downDownNodeIsEndAr = null;
		if (nodeAdopt) {
			// 如果下级流程节点是最后结束节点,则直接改变实体状态为审核通过
			if (downNodeIsEndAr.getData()) {
				String verifyStatus = CheckStatusType.AUDITED.getCode();
				auditedEditModel(verifyDto.getId(), flowTaskType, user.getUserId(), strChangeId.toString(), verifyStatus,orderDetId.toString());
			} else {
				// 找到下一个节点的id
				ApiResult<?> downNodeIdAr = flowService.upOrDownNodeId(flow, flowTemp.getFlowData(),currNotice.getFlowTempNodeId(), 1);
				if (!downNodeIdAr.isSuccess()) {
					return downNodeIdAr;
				}
				// 下下一个节点是否是结束节点（下一个节点是否是终审节点）
				downDownNodeIsEndAr = flowService.nodeNextNodeIsEnd(flow, flowTemp.getFlowData(),downNodeIdAr.getData().toString());
				if (!downDownNodeIsEndAr.isSuccess()) {
					return downDownNodeIsEndAr;
				}
				MsgNoticeDto noticeTask = new MsgNoticeDto();
				noticeTask.setType(MsgType.TASK.getCode());
				noticeTask.setTaskType(flowTaskType.getTaskTypeId().toString());
				noticeTask.setPushUserId(downTaskPushUserId.toString());
				noticeTask.setTitle(taskTypeName + "待" + (downDownNodeIsEndAr.getData() ? "(终审)" : "审核") + "通知");
				StringBuffer sbTask = new StringBuffer().append("需要" + (downDownNodeIsEndAr.getData() ? "(终审)的" : "审核的")).append(taskFromReamrks);
				noticeTask.setContent(sbTask.toString());
				noticeTask.setFlowFromId(verifyDto.getId());
				noticeTask.setFlowVerifyStatus(CheckStatusType.WAIT_AUDITED.getCode());
				noticeTask.setFlowVerifyReason("上级审核节点审核通过,自动进入下一级" + taskTypeName + (downDownNodeIsEndAr.getData() ? "(终审)" : "审核") + "节点");
				noticeTask.setFlowTempId(flowTemp.getFlowTempId());
				noticeTask.setFlowTempNodeId(currNotice.getFlowTempNodeId());
				noticeTask.setFlowSubmitNo(currNotice.getFlowSubmitNo());
				ApiResult<?> apiResultTask = save(noticeTask);
				if (!apiResultTask.isSuccess()) {
					throw new RRException(apiResultTask.getMsg());
				}
			}
		}
		// 处理当前审核任务
		msgNoticeReadDao.updateReadAndVerifyStatus(verifyDto.getReadId(), CheckStatusType.AUDITED.getCode(),verifyDto.getRejectReason(), new Date());
		RedisServer.remove(null,RedisKeyConstant.KEY_MSG_NOTICE + MsgType.TASK.toString().toLowerCase()+flowTaskType.getTaskTypeId() +":"+user.getUserId());
		if (nodeAdopt) {
			// 将该节点其他任务的任务改为已处理
			otherAudited(currNotice.getNoticeId(),user,flowTaskType.getTaskTypeId().toString());
		}
		StringBuffer resultMsg = new StringBuffer("操作成功(审核通过)!");
		//是否会签0 不是 1是
		int verifyJoint = currNode.getIntValue("verifyJoint");
		if (verifyJoint == 0) {
			if (downNodeIsEndAr.getData()) {
				resultMsg.append("该节点是'终审'节点,该流程自动结束.");
			} else {
				if (downDownNodeIsEndAr != null) {
					resultMsg.append("该节点是'非终审'节点,流程自动进入下一级" + taskTypeName + (downDownNodeIsEndAr.getData() ? "(终审)" : "审核") + "节点.");
				} else {
					resultMsg.append("该节点是'非终审'节点,流程自动进入下一级审核节点.");
				}
			}
		} else if (verifyJoint == 1){
			if (nodeAdopt) {
				if (downNodeIsEndAr.getData()) {
					resultMsg.append("该节点是'会签'终审节点,该流程自动结束.");
				} else {
					if (downDownNodeIsEndAr != null) {
						resultMsg.append("该节点是'会签'非终审节点,流程自动进入下一级" + taskTypeName + (downDownNodeIsEndAr.getData() ? "(终审)" : "审核") + "节点.");
					} else {
						resultMsg.append("该节点是'会签'非终审节点,流程自动进入下一级审核节点.");
					}
				}
			} else {
				resultMsg.append("该节点是'会签'" + taskTypeName + (downNodeIsEndAr.getData() ? "(终审)" : "(非终审)") + "节点,目前还差");
				resultMsg.append((needNum - actualNum));
				resultMsg.append("人(").append(verifyCheckAr.getData().toString()).append(")处理.");
			}
			sb.append("<br/>会签进度: " + needNum + "/" + actualNum);
		}
		// 业务操作日志
		auditedAddModelLog(verifyDto.getId(), user.getUserId(), orderId.toString(),orderDetId.toString(), sb.toString());
		return ApiResult.data(resultMsg.toString());
	}
	/**
	 *  将该节点其他任务的任务改为已处理（通过）
	 * @param noticeId
	 * @param user
	 */
	public void otherAudited(String noticeId,LoginUser user,String taskType) {
		List<MsgNoticeRead> otherMsgs = msgNoticeReadDao.getByNoticeIdVerifyStatus(noticeId);
		String otherReason = "该任务已被他人(" + (StringUtilsV2.isNotBlank(user.getUserName()) ? user.getUserName() : user.getUserAcct()) + "[" + user.getOrgName() + "])核准";
		for (MsgNoticeRead msgNoticeRead : otherMsgs) {
			msgNoticeReadDao.updateReadAndVerifyStatus(msgNoticeRead.getReadId(), CheckStatusType.AUDITED.getCode(), otherReason, null);
			RedisServer.remove(null,RedisKeyConstant.KEY_MSG_NOTICE + MsgType.TASK.toString().toLowerCase()+taskType + ":" + msgNoticeRead.getReadUserId());
		}
	}
	/**
	 * 流程节点审核驳回
	 * 
	 * @param verifyDto
	 * @return
	 */
	
	@Transactional
	public ApiResult<?> flowRefused(VerifyDto verifyDto) {
		LoginUser user = UserUtil.getLoginUser();
		MsgNoticeRead read = new MsgNoticeRead();
		MsgNotice currNotice = new MsgNotice();
		JSONObject currNode = new JSONObject();
		FlowDesignTemp flowTemp = new FlowDesignTemp();
		JSONObject flow = new JSONObject();
		ApiResult<Boolean> downNodeIsEndAr = new ApiResult<Boolean>();
		StringBuffer needVerifyAuditedNum = new StringBuffer();
		StringBuffer actualVerifyAuditedNum = new StringBuffer();
		ApiResult<?> verifyCheckAr = verifyCheck(verifyDto, user.getUserId(), read, currNotice, currNode, flowTemp,flow, downNodeIsEndAr, needVerifyAuditedNum, actualVerifyAuditedNum,user);
		if (!verifyCheckAr.isSuccess()) {
			return verifyCheckAr;
		}
		String currNodeName = StringUtilsV2.isNotBlank(currNode.getString("name")) ? currNode.getString("name") : currNotice.getFlowTempNodeId();
		FlowTaskType flowTaskType= flowTaskTypeDao.getById(currNotice.getTaskType());
		if(flowTaskType == null){
			return ApiResult.error("通过类型ID("+currNotice.getTaskType()+")未找到对应流程业务类型");
		}
		StringBuffer taskTypeName = new StringBuffer();// 业务类型名
		StringBuffer taskFromReamrks = new StringBuffer();// 业务主体说明
		StringBuffer downTaskPushUserId = new StringBuffer();// 下一级任务提交人
		List<String> collectUserIds = new ArrayList<>();// 审核结果通知接收人
		StringBuffer strChangeId = new StringBuffer();// 换刊创意id
		StringBuffer orderId = new StringBuffer();// 主订单id(换刊需要)
		StringBuffer orderDetId = new StringBuffer();// 子订单id(换刊需要)
		ApiResult<?> verifyInfoAr = verifyInfo(verifyDto.getId(), flowTaskType, taskTypeName, taskFromReamrks,downTaskPushUserId, collectUserIds, strChangeId, orderId,orderDetId);
		if (!verifyInfoAr.isSuccess()) {
			return verifyInfoAr;
		}
		String verifyStatus = CheckStatusType.REFUSED.getCode();//驳回状态码
		auditedEditModel(verifyDto.getId(), flowTaskType, user.getUserId(), strChangeId.toString(), verifyStatus,orderDetId.toString());
		MsgNoticeDto notice = new MsgNoticeDto();
		notice.setType(MsgType.RESULT.getCode());
		notice.setTaskType(flowTaskType.getTaskTypeId().toString());
		notice.setTitle(taskTypeName + "审核结果(" + (downNodeIsEndAr.getData() ? "'终审'" : "'非终审'") + "被驳回)通知");
		notice.setCollectUserIds(collectUserIds);// 接收人
		StringBuffer sb = new StringBuffer()
				//.append("业务模板: ").append(taskTypeName).append(",<br>")
				.append("审核流程: ").append(flowTemp.getFlowName()).append(",<br>")
				.append("任务节点: ").append(currNodeName).append(",<br/>")
				.append("是否为终审: ").append(downNodeIsEndAr.getData() ? "是" : "不是").append(",<br>")
				.append("审核结果: ").append("审核被驳回").append(",<br/>")
				.append("审核说明: ").append(verifyDto.getRejectReason()).append(".<br>");
		StringBuffer reamrksPrefix = new StringBuffer("审核通过的").append(taskTypeName).append("详情: <br>");
		notice.setContent(sb.append(reamrksPrefix).append(taskFromReamrks).toString());
		notice.setFlowFromId(verifyDto.getId());
		notice.setFlowVerifyStatus(CheckStatusType.REFUSED.getCode());
		notice.setFlowVerifyReason(verifyDto.getRejectReason());
		notice.setFlowSubmitNo(currNotice.getFlowSubmitNo());
		notice.setFlowTempId(currNotice.getFlowTempId());
		notice.setFlowTempNodeId(currNotice.getFlowTempNodeId());
		ApiResult<?> apiResult = save(notice);
		if (!apiResult.isSuccess()) {
			throw new RRException(apiResult.getMsg());
		}
		// 写入操作日志
		auditedAddModelLog(verifyDto.getId(), user.getUserId(), orderId.toString(),orderDetId.toString(), sb.toString());
		// 处理当前审核任务
		msgNoticeReadDao.updateReadAndVerifyStatus(read.getReadId(), CheckStatusType.REFUSED.getCode(),verifyDto.getRejectReason(), new Date());
		// 将该节点其他任务的任务改为已处理
		otherRefused(currNotice.getNoticeId(),user,flowTaskType.getTaskTypeId().toString());
		return ApiResult.ok();
	}
	/**
	 *  将该节点其他任务的任务改为已处理（驳回）
	 * @param noticeId
	 * @param user
	 */
	public void otherRefused(String noticeId,LoginUser user,String taskType) {
		// 将该节点其他任务的任务改为已处理
		List<MsgNoticeRead> otherMsgs = msgNoticeReadDao.getByNoticeIdVerifyStatus(noticeId);
		String otherReason = "该任务已被他人(" + (StringUtilsV2.isNotBlank(user.getUserName()) ? user.getUserName() : user.getUserAcct()) + "[" + user.getOrgName() + "])驳回";
		RedisServer.remove(null,RedisKeyConstant.KEY_MSG_NOTICE + MsgType.TASK.toString().toLowerCase()+taskType+ ":" + user.getUserId());
		for (MsgNoticeRead msgNoticeRead : otherMsgs) {
			msgNoticeReadDao.updateReadAndVerifyStatus(msgNoticeRead.getReadId(), CheckStatusType.REFUSED.getCode(),otherReason, null);
			RedisServer.remove(null,RedisKeyConstant.KEY_MSG_NOTICE + MsgType.TASK.toString().toLowerCase()+taskType+ ":" + msgNoticeRead.getReadUserId());
		}
	}
	/**
	 * 流程节点审核转办
	 * 
	 * @param verifyDto
	 * @return
	 */
	
	@Transactional
	public ApiResult<?> flowDoof(VerifyDto verifyDto) {
		LoginUser user = UserUtil.getLoginUser();
		if (verifyDto.getDoofUserId().equals(user.getUserId())) {
			return ApiResult.error("要转办给的用户不能是自己");
		}
		MsgNoticeRead read = new MsgNoticeRead();
		MsgNotice currNotice = new MsgNotice();
		JSONObject currNode = new JSONObject();
		FlowDesignTemp flowTemp = new FlowDesignTemp();
		JSONObject flow = new JSONObject();
		ApiResult<Boolean> downNodeIsEndAr = new ApiResult<Boolean>();
		StringBuffer needVerifyAuditedNum = new StringBuffer();
		StringBuffer actualVerifyAuditedNum = new StringBuffer();
		ApiResult<?> verifyCheckAr = verifyCheck(verifyDto, user.getUserId(), read, currNotice, currNode, flowTemp, flow, downNodeIsEndAr, needVerifyAuditedNum, actualVerifyAuditedNum,user);
		if (!verifyCheckAr.isSuccess()) {
			return verifyCheckAr;
		}
		String currNodeName = StringUtilsV2.isNotBlank(currNode.getString("name")) ? currNode.getString("name") : currNotice.getFlowTempNodeId();
		FlowTaskType flowTaskType= flowTaskTypeDao.getById(currNotice.getTaskType());
		if(flowTaskType == null){
			return ApiResult.error("通过类型ID("+currNotice.getTaskType()+")未找到对应流程业务类型");
		}
		// 转发任务消息
		MsgNoticeRead msgNoticeRead = new MsgNoticeRead();
		msgNoticeRead.setNoticeId(read.getNoticeId());
		msgNoticeRead.setFlowVerifyStatus(CheckStatusType.WAIT_AUDITED.getCode());
		msgNoticeRead.setFlowVerifyReason("该任务由他人[" + user.getUserName() + "(" + user.getOrgName() + ")]转办的" + flowTaskType.getTaskTypeName() + (downNodeIsEndAr.getData() ? "终审" : "审核") + "节点");
		msgNoticeRead.setReadUserId(verifyDto.getDoofUserId());
		msgNoticeReadDao.save(msgNoticeRead);
		// 写入日志操作
		StringBuffer taskTypeName = new StringBuffer();// 业务类型名
		StringBuffer taskFromReamrks = new StringBuffer();// 业务主体说明
		StringBuffer downTaskPushUserId = new StringBuffer();// 下一级任务提交人
		List<String> collectUserIds = new ArrayList<>();// 审核结果通知接收人
		StringBuffer strChangeId = new StringBuffer();// 换刊创意id
		StringBuffer orderId = new StringBuffer();// 主订单id(换刊需要)
		StringBuffer orderDetId = new StringBuffer();// 子订单id(换刊需要)
		ApiResult<?> verifyInfoAr = verifyInfo(verifyDto.getId(), flowTaskType, taskTypeName, taskFromReamrks, downTaskPushUserId, collectUserIds, strChangeId, orderId,orderDetId);
		if (!verifyInfoAr.isSuccess()) {
			return verifyInfoAr;
		}
		SysUserDetailVO doofUser = userDao.getById(verifyDto.getDoofUserId());
		if (doofUser == null) {
			return ApiResult.error("没有找到要转办给的用户信息");
		}
		StringBuffer sb = new StringBuffer("流程被转办<br/>")
		.append("审核流程: ").append(flowTemp.getFlowName()).append(",<br>")
		.append("任务节点: ").append(currNodeName).append(",<br/>")
		.append("是否为终审: ").append(downNodeIsEndAr.getData() ? "是" : "不是").append(",<br>")
		.append("转办用户: ").append((StringUtilsV2.isNotBlank(user.getUserName()) ? user.getUserName() : user.getUserAcct()) + "[" + user.getOrgName() + "],<br/>")
		.append("转给用户: ").append((StringUtilsV2.isNotBlank(doofUser.getUserName()) ? doofUser.getUserName() : doofUser.getUserAcct()) + "[" + doofUser.getOrgName() + "].<br/>");
		/*if(taskType.equals(MsgTaskType.CHANGE)) {
			sb.append("转办对应").append(taskTypeName).append("任务详情:  <br/>").append(taskFromReamrks);
		}*/
		// 操作日志
		auditedAddModelLog(verifyDto.getId(), user.getUserId(), orderId.toString(),orderDetId.toString(), sb.toString());
		// 处理当前审核任务
		msgNoticeReadDao.updateReadAndVerifyStatus(read.getReadId(), CheckStatusType.DOOFFICE.getCode(), verifyDto.getRejectReason(), new Date());
		RedisServer.remove(null,RedisKeyConstant.KEY_MSG_NOTICE + MsgType.TASK.toString().toLowerCase()+flowTaskType.getTaskTypeId()+":"+user.getUserId());// 本用户
		RedisServer.remove(null,RedisKeyConstant.KEY_MSG_NOTICE + MsgType.TASK.toString().toLowerCase()+flowTaskType.getTaskTypeId()+":"+verifyDto.getDoofUserId());// 转办给的用户
		return ApiResult.ok();
	}

	private ApiResult<?> verifyCheck(VerifyDto verifyDto, String verifyUserId, MsgNoticeRead read, MsgNotice currNotice,
			JSONObject currNode, FlowDesignTemp flowTemp, JSONObject flow, ApiResult<Boolean> downNodeIsEndAr,
			StringBuffer needVerifyAuditedNum, StringBuffer actualVerifyAuditedNum,LoginUser user) {
		if (verifyDto == null) {
			return ApiResult.error("操作失败,审核操作信息不能为空");
		}
		// 检查该任务是否被处理过了
		if (StringUtilsV2.isBlank(verifyDto.getReadId())) {
			return ApiResult.error("操作失败,任务ID不能为空");
		}
		MsgNoticeRead readNew = msgNoticeReadDao.getById(verifyDto.getReadId());
		if (readNew == null) {
			return ApiResult.error("操作失败,该任务消息对应的任务数据不存在");
		}
		BeanUtils.copyProperties(readNew, read);
		MsgNotice currNoticeNew = msgNoticeDao.getById(read.getNoticeId());
		if (currNoticeNew == null) {
			return ApiResult.error("操作失败,要执行的任务数据不存在");
		}
		if (!MsgType.TASK.getCode().equals(currNoticeNew.getType())) {
			return ApiResult.error("操作失败,该消息不是任务消息");
		}
		BeanUtils.copyProperties(currNoticeNew, currNotice);
		if (StringUtilsV2.isBlank(verifyDto.getId())) {
			verifyDto.setId(currNotice.getFlowFromId());
		}
		FlowDesignTemp flowTempNew = flowService.getTempById(currNotice.getFlowTempId());
		if (flowTempNew == null) {
			return ApiResult.error("操作失败,未找到流程设计模板数据");
		}
		BeanUtils.copyProperties(flowTempNew, flowTemp);
		if (StringUtilsV2.isNotBlank(flowTemp.getFlowData()) && JSONUtils.mayBeJSON(flowTemp.getFlowData())) {
			JSONObject flowNew = JSONObject.parseObject(flowTemp.getFlowData());
			// BeanUtils.copyProperties(flowNew, flow);
			Set<String> keys = flowNew.keySet();
			for (String key : keys) {
				flow.put(key, flowNew.get(key));
			}
		}
		ApiResult<JSONObject> currNodeAr = flowService.getNode(flow, flowTemp.getFlowData(), currNotice.getFlowTempNodeId());
		if (!currNodeAr.isSuccess()) {
			return currNodeAr;
		}
		String currNodeName;
		JSONObject currNodeNew = currNodeAr.getData();
		if (currNodeNew != null) {
			Set<String> keys = currNodeNew.keySet();
			for (String key : keys) {
				currNode.put(key, currNodeNew.get(key));
			}
			currNodeName = currNodeNew.getString("name");
		} else {
			currNodeName = currNotice.getFlowTempNodeId();
		}
		// 该节点所有的任务消息
		List<MsgNoticeReadToUserVO> rlist = msgNoticeReadDao.getByNoticeId(read.getNoticeId());
		MsgNoticeReadToUserVO currUserVerifyRead = null;// 当前用户的处理消息
		MsgNoticeReadToUserVO refusedVerifyRead = null;// 当前节点的驳回处理消息
		List<SelectValueVO> auditedUsers = new ArrayList<>();// 当前节点的所有同意处理消息
		List<String> waitAuditedUserNames = new ArrayList<>();// 当前节点的未处理消息的用户
		if (rlist.size() > 0) {
			for (MsgNoticeReadToUserVO verifyRead : rlist) {
				if (verifyUserId.equals(verifyRead.getReadUserId())) {
					currUserVerifyRead = verifyRead;
				}
				if (CheckStatusType.REFUSED.getCode().equals(verifyRead.getFlowVerifyStatus())) {
					refusedVerifyRead = verifyRead;
					break;
				}
				if (CheckStatusType.AUDITED.getCode().equals(verifyRead.getFlowVerifyStatus())) {
					auditedUsers.add(new SelectValueVO(verifyRead.getReadUserId(), verifyRead.getReadUserName(),verifyRead.getFlowVerifyReason()));
				}
				if (CheckStatusType.WAIT_AUDITED.getCode().equals(verifyRead.getFlowVerifyStatus()) && !user.getUserId().equals(verifyRead.getReadUserId())) {
					waitAuditedUserNames.add(verifyRead.getReadUserName());
				}
			}
			if (currUserVerifyRead == null) {
				return ApiResult.error("操作失败,您没有该流程(" + flowTemp.getFlowName() + ")该审核节点(" + currNodeName + ")的审核权限");
			} else if (!CheckStatusType.WAIT_AUDITED.getCode().equals(currUserVerifyRead.getFlowVerifyStatus())) {
				return ApiResult.error("操作失败,您之前已经处理过该流程(" + flowTemp.getFlowName() + ")该审核节点(" + currNodeName + "),处理结果:" + CheckStatusType.getName(currUserVerifyRead.getFlowVerifyStatus()) + ",处理说明:" + currUserVerifyRead.getFlowVerifyReason());
			}
			if (refusedVerifyRead != null) {
				return ApiResult.error("操作失败," + refusedVerifyRead.getReadUserName() + "之前已经处理过该流程(" + flowTemp.getFlowName() + ")该审核节点(" + currNodeName + "),处理结果:" + CheckStatusType.getName(refusedVerifyRead.getFlowVerifyStatus()) + ",处理说明:" + refusedVerifyRead.getFlowVerifyReason());
			}
		} else {
			return ApiResult.error("操作失败,该流程(" + flowTemp.getFlowName() + ")该审核节点(" + currNodeName + ")目前没有审核任务消息");
		}
		String verifyType = currNode.getString("verifyType");
		if (StringUtilsV2.isBlank(verifyType)) {
			return ApiResult.error("操作失败,流程(" + flowTemp.getFlowName() + ")审核节点(" + currNodeName + ")的任务归属类型为空");
		}
		String verifyModelIds = currNode.getString("verifyModelIds");
		if (StringUtilsV2.isBlank(verifyModelIds)) {
			return ApiResult.error("操作失败,流程(" + flowTemp.getFlowName() + ")审核节点(" + currNodeName + ")的任务归属者为空");
		}
		List<String> collectUserIds = new ArrayList<>();
		if ("3".equals(verifyType)) {//用户
			collectUserIds = getUsers(Arrays.asList(verifyModelIds.split(",")), null, null).getData();
			if (CollectionUtils.isEmpty(collectUserIds)) {
				return ApiResult.error("操作失败,流程(" + flowTemp.getFlowName() + ")的审核节点(" + currNodeName + ")未指定接收审核任务的用户");
			}
		} else if ("2".equals(verifyType)) {//角色
			collectUserIds = getUsers(null, Arrays.asList(verifyModelIds.split(",")), null).getData();
			if (CollectionUtils.isEmpty(collectUserIds)) {
				return ApiResult.error("操作失败,流程(" + flowTemp.getFlowName() + ")的审核节点(" + currNodeName + ")下指定角色(" + currNode.getString("verifyModelNames") + ")下未找到接收审核任务的用户");
			}
		} else if ("1".equals(verifyType)) {//机构
			collectUserIds = getUsers(null, null, Arrays.asList(verifyModelIds.split(","))).getData();
			if (CollectionUtils.isEmpty(collectUserIds)) {
				return ApiResult.error("操作失败,流程(" + flowTemp.getFlowName() + ")的审核节点(" + currNodeName + ")下指定机构(" + currNode.getString("verifyModelNames") + ")下未找到接收审核任务的用户");
			}
		}
		int auditedUsersNum = auditedUsers.size();//实际已处理该节点的用户数
		int verifyJoint = currNode.getIntValue("verifyJoint");//是否会签节点 0不是 1是
		if (auditedUsersNum > 0) {
			if (verifyJoint == 0) {
				SelectValueVO auditedSelect = auditedUsers.get(0);
				return ApiResult.error("操作失败," + auditedSelect.getName() + "之前已经处理过该流程(" + flowTemp.getFlowName() + ")该审核节点(" + currNodeName + "),处理结果:" + CheckStatusType.AUDITED.getName() + ",处理说明:" + auditedSelect.getValue());
			} else if (collectUserIds.size() == auditedUsersNum) {
				return ApiResult.error("操作失败,该流程(" + flowTemp.getFlowName() + ")该审核节点(" + currNodeName + ")是\"会签\"节点,该节点所有任务归属者(" + currNode.getString("verifyModelNames") + ")已全部做了'核准'处理");
			}
		}
		actualVerifyAuditedNum.append(auditedUsersNum);
		if (verifyJoint == 0) {
			needVerifyAuditedNum.append(1);
		} else if (verifyJoint == 1) {
			needVerifyAuditedNum.append(collectUserIds.size());
		} else{
			needVerifyAuditedNum.append(collectUserIds.size());
		}
		ApiResult<Boolean> downNodeIsEndArNew = flowService.nodeNextNodeIsEnd(flow, flowTemp.getFlowData(),currNotice.getFlowTempNodeId());
		if (!downNodeIsEndArNew.isSuccess()) {
			return downNodeIsEndArNew;
		}
		downNodeIsEndAr.setData(downNodeIsEndArNew.getData());
		return ApiResult.data(String.join(",", waitAuditedUserNames));
	}

	/**
	 * 流程节点审核通过后组装结果参数+下一级节点的发送人
	 * @param flowFromId
	 * @param taskType
	 * @param taskTypeName
	 * @param taskFromReamrks
	 * @param downTaskPushUserId
	 * @param collectUserIds
	 * @param strChangeId
	 * @param orderId
	 * @return
	 */
	private ApiResult<?> verifyInfo(String flowFromId, FlowTaskType taskType, StringBuffer taskTypeName,
			StringBuffer taskFromReamrks, StringBuffer downTaskPushUserId, List<String> collectUserIds,
			StringBuffer strChangeId, StringBuffer orderId, StringBuffer orderDetId) {
		taskTypeName.append(taskType.getTaskTypeName());
		String waitVerifyStatus = CheckStatusType.WAIT_AUDITED.getCode();//审核中状态码
		StringBuffer verifyCheck = new StringBuffer();
		boolean alreadyVerify = false;
		//当前只拓展了素材和客户模块，如果有其他业务模块继续在这里拓展
		if (taskType.getModel().equals(AdContent.class.getSimpleName())) {
			waitVerifyStatus = CheckStatusType.WAIT_AUDITED.getCode();//审核中状态码
			AdContentVO taskModel = adContentDao.getById(flowFromId);
			if (taskModel == null) {
				return ApiResult.error("未找到要审核的‘" + taskTypeName+"’信息");
			}
			if(!waitVerifyStatus.equals(taskModel.getStatus())) {
				alreadyVerify = true;
				if(taskModel.getVerifyTime() != null)
					verifyCheck.append("审核人员:").append(taskModel.getVerifyUserName()).append("<br/>")
					.append("审核时间:").append(CommUtil.formatLongDate(taskModel.getVerifyTime())).append("<br/>");
			}else {
				downTaskPushUserId.append(taskModel.getSubmitterId());
				if (downTaskPushUserId.length() == 0) {
					downTaskPushUserId.append(taskModel.getCreateUserId());
				}
				collectUserIds.add(taskModel.getSubmitterId());
				collectUserIds.add(taskModel.getCreateUserId());
				taskFromReamrks
				.append(taskTypeName).append("ID: ").append(taskModel.getIndexId()).append(",<br/>" )
				.append(taskTypeName).append("类型: ").append(taskModel.getAdTypeName()).append(",<br/>")
				.append(taskTypeName).append("名称: ").append(taskModel.getAdName());
			}
		} if (taskType.getModel().equals(Customer.class.getSimpleName())) {
			CustomerVO taskModel = customerDao.getById(flowFromId);
			if (taskModel == null) {
				return ApiResult.error("未找到要审核的‘" + taskTypeName+"’信息");
			}
			if(!waitVerifyStatus.equals(taskModel.getStatus().toString())) {
				alreadyVerify = true;
				if(taskModel.getVerifyTime() != null)
					verifyCheck.append("审核人员:").append(taskModel.getVerifyUserName()).append("<br/>")
					.append("审核时间:").append(CommUtil.formatLongDate(taskModel.getVerifyTime())).append("<br/>");
			}else {
				downTaskPushUserId.append(taskModel.getSubmitterId());
				if (downTaskPushUserId.length() == 0) {
					downTaskPushUserId.append(taskModel.getCreateUserId());
				}
				collectUserIds.add(taskModel.getSubmitterId());
				collectUserIds.add(taskModel.getCreateUserId());
				collectUserIds.add(taskModel.getFollowUserId());
				taskFromReamrks
				.append(taskTypeName).append("ID: ").append(taskModel.getIndexId()).append(",<br/>")
				.append(taskTypeName).append("名称: ").append(taskModel.getCompanyName());
			}
		} 
		if(alreadyVerify) {
			StringBuffer pix = new StringBuffer().append("当前流程对应的").append(taskTypeName).append("已经被他人从旧版本审核功能(审核管理->").append(taskTypeName).append("审核)审核过了,").append("<br/>");
			verifyCheck.insert(0, pix).append("具体审核记录请查看该").append(taskTypeName).append("的操作日志");
			return ApiResult.error(verifyCheck.toString());
		}
		return ApiResult.ok();
	}

	/**
	 * 流程节点审核通过后修改业务实体为审核通过
	 * 
	 * @param flowFromId
	 * @param taskType
	 * @param loginUserId
	 * @param strChangeId
	 */
	private void auditedEditModel(String flowFromId, FlowTaskType taskType, String loginUserId, String strChangeId,
			String veriyStatus,String orderDetId) {
		//当前只拓展了素材和客户模块，如果有其他业务模块继续在这里拓展
		if (taskType.getModel().equals(AdContent.class.getSimpleName())) {
			AdContent adContent = new AdContent();
			adContent.setAdId(flowFromId);
			adContent.setVerifyUserId(loginUserId);
			adContent.setVerifyTime(new Date());
			adContent.setStatus(veriyStatus);
			adContentDao.update(adContent);
		} else if (taskType.getModel().equals(Customer.class.getSimpleName())) {
			Customer customer = new Customer();
			customer.setCustomerId(flowFromId);
			customer.setStatus(CommUtil.null2Int(veriyStatus));
			customer.setVerifyUserId(loginUserId);
			customer.setVerifyTime(new Date());
			customerDao.update(customer);
		} 
	}

	/**
	 * 流程节点审核通过后新增业务实体为的操作记录
	 * 
	 * @param flowFromId
	 * @param taskType
	 * @param loginUserId
	 * @param orderId
	 * @param logInfo
	 */
	private void auditedAddModelLog(String flowFromId, String loginUserId, String orderId,String orderDetId,String logInfo) {
		logService.saveAuditLogs(new SysAuditLogs(loginUserId, CommUtil.getDateJJSecond(new Date(), 1), logInfo, flowFromId));
	}
	/**
	 * 消息对应业务实体详情
	 */
	public ApiResult<?> readGet(String readId) {
		if (readId == null) {
			return ApiResult.error("请传入readId");
		}
		MsgNoticeRead read = msgNoticeReadDao.getById(readId);
		if (read == null) {
			return ApiResult.error("该任务不存在");
		}
		MsgNotice msgNotice = msgNoticeDao.getById(read.getNoticeId());
		if (msgNotice == null) {
			return ApiResult.error("该任务不存在");
		}
		FlowTaskType flowTaskType= flowTaskTypeDao.getById(msgNotice.getTaskType());
		if(flowTaskType == null){
			return ApiResult.error("通过类型ID("+msgNotice.getTaskType()+")未找到对应流程业务类型");
		}
		//当前只拓展了素材和客户模块，如果有其他业务模块继续在这里拓展
		if (flowTaskType.getModel().equals(AdContent.class.getSimpleName())) {
			return ApiResult.data(adContentDao.getById(msgNotice.getFlowFromId()));
		} else if (flowTaskType.getModel().equals(Customer.class.getSimpleName())) {
			return ApiResult.data(customerDao.getById(msgNotice.getFlowFromId()));
		}
		
		return ApiResult.ok();
	}

	
	public void updateReadAndVerifyStatusByFromId(String flowFromId, String flowVerifyStatus, String flowVerifyReason) {
		try {
			Set<SelectVO> sv = msgNoticeReadDao.getReadByFromId(flowFromId);
			for (SelectVO s : sv) {
				RedisServer.remove(null,RedisKeyConstant.KEY_MSG_NOTICE  + MsgType.TASK.toString().toLowerCase() + s.getName() + ":" + s.getId());
			}
			msgNoticeReadDao.updateReadAndVerifyStatusByFromId(flowFromId, flowVerifyStatus, flowVerifyReason, new Date());
		} catch (Exception e) {
			log.error(flowVerifyReason+",发生异常:"+e.getMessage());
		}
	}
	
	
	public void delReadStatusByFromId(String flowFromId) {
		Set<SelectVO> sv = msgNoticeReadDao.getReadByFromId(flowFromId);
		for (SelectVO s : sv) {
			RedisServer.remove(null,RedisKeyConstant.KEY_MSG_NOTICE  + MsgType.TASK.toString().toLowerCase() + s.getName() + ":" + s.getId());
		}
		msgNoticeReadDao.delReadStatusByFromId(flowFromId);
	}
}
